#include "Enums.h"
#include "BBox3D.h"
#include "Accelerators.h"
#include "ModelControler.h"
#include "ViewManager.h"
#include "BaseHandle.h"
#include "Mesh.h"
#include "Field.h"
#include "Object.h"
#include "VtxAccumulator.h"
#include "WindowFV.h"
#include "fv_compiler.h"
#include "fv_config.h"
#include "fv_exception.h"
#include "fv_assert.h"
#include "fv_txt_utls.h"
#include "fv_timer.h"
#include "defs.h"
#include "Log.h"
#include "dbg.h"
#include <sstream>
#include "mmh_intf.h"
#include "ocl/ocl.h"
#ifdef PARALLEL
#include "mmph_intf.h"
#include "pch_intf.h"
#endif

namespace FemViewer {

	static const int DefaultMeshType 		 = MeshPrizm;
	static const int DefaultAppoximationType = FieldDG;

	ModelCtrl& ModelCtrlInst(void)
	{
		mfp_debug("ModelCtrInst\n");
		static ModelCtrl modelCtrlImpl;
		return modelCtrlImpl;
	}

	ModelCtrl::ModelCtrl()
	: _mesh()
	, _field(_mesh)
	, _pRC(NULL)
	, _mId(_mesh.Id())
	, _fId(_field.Id())
	, _curPlane()
	//, _currLegend(/*ViewManagerInst().GetLegend()*/)
	, _hosts(NULL)
	{
		mfp_debug("ModelCtrl ctr\n");
		int numHosts(1);
#ifdef PARALLEL
		assert(pcr_is_parallel_initialized() == 1);
		_procId 	 = pcr_my_proc_id();
		_nrProcesses = pcr_nr_proc();
		numHosts = _nrProcesses;
#endif
		InitHosts(numHosts);
	}

	ModelCtrl::~ModelCtrl()
	{
		mfp_debug("ModelCtrl dtr\n");
		FV_FREE_PTR(_pRC);
		FV_FREE_ARR(_hosts);
	}


	bool ModelCtrl::InitMesh(const std::string& meshFile_)
	{
		try {
			mfp_debug("ModelCtrl: InitMesh\n");
			if (_mesh.Init(meshFile_)<0) throw fv_exception("Can't init mesh!");
			if (! _mesh.IsGeometryInit()) {
				_mesh.GetGLHandle() = ViewManagerInst().GetGraphicData().GetRootObject().AddNewObject(meshFile_,&(_mesh.GetGLid()));
			}
			_mesh.RenderWireframe();
			FV_CHECK_ERROR_GL();

		}
		catch (const fv_exception& ex)
		{
			IO::log(ex.what());
			return false;
		}
		return true;
	}

	bool ModelCtrl::InitField(Mesh* parent_mesh_,const std::string& fieldFile_,const HandleType type_)
	{
		try {
			if (!parent_mesh_) throw fv_exception("NULL pointer to parent mesh!\n");
		    if (_field.Init(-1,fieldFile_)<0) throw fv_exception("Can't init field\n");
			if (! _field.IsGeometryInit()) _field.GetGLHandle() = ViewManagerInst().GetGraphicData().GetRootObject().AddNewObject(fieldFile_,&(_field.GetGLId()));
			_field.Draw();
			//double min_v = _field.GetValues().first;
			//double max_v = _field.GetValues().second;
			//ViewManagerInst().GetLegend().Set(min_v,max_v,min_v,max_v);
			//mfp_debug("After set legend");
			
		    //_field.Render(NULL,NULL);
			//_field.Render2();
			//_field.Render3();

			FV_CHECK_ERROR_GL();
		} 
		catch(const fv_exception& ex)
		{
			IO::log(ex.what());
			return false;
		}

		return true;
	}


	void ModelCtrl::Clear()
	{
		mfp_debug("Clear ModelCtrl\n");
		this->_mesh   .Reset();		// reset mesh object
		this->_field  .Reset();		// reset field object
		FV_FREE_PTR(_pRC); 			// reset rendering context
		//this->_vEGL.clear();		// clear vector of graphic elements
		this->_curPlane.Reset();	// set to default cutting plane
		ViewManagerInst().Reset();  // reset view-manger
		ViewManagerInst().GetLegend().Create(); // recreate the legend
	}

	void ModelCtrl::EraseMesh(const int & id_)
	{
		//BaseHandle*& hMesh = _objArray.at(id_);

		//for (size_type fid(0);fid<_objArray.Size();++fid)
		//{
		//	if (!_objArray[fid] || (_objArray[fid]->type() & FieldHandle)) continue;
		//	this->EraseField(fid);
		//}
		//FV_FREE_PTR(hMesh);
		assert(!"Not Implemented");

	}

	void ModelCtrl::EraseField(const int& id_)
	{
		assert(!"Not Implemented");
	}

	bool ModelCtrl::Do(const eCommnad oper,const std::string& str)
	{
		switch (oper)
		{
		// Do all init jobs
			case INIT:
			{
				// Test conections
				if(!TestConnection()) {
					#ifdef PARALLEL
					mfp_debug("Exit because of wrong test connection\n");
					pcr_exit_parallel();
					#endif
				}

				// Try init mesh data and then field data
				if (this->Do(INITMESH))
				{
				#ifdef PARALLEL
					IO::printSynchronizedParallel("Hello\n");
					const int l = 6;
					const int MCTRL_TAG_ID = 33456;
					BBox3D lbbox(_mesh.GetMeshBBox3D());
					float tab[l];
					if (this->_procId == pcr_print_master()) {

						for(int i = 1; i <= this->_nrProcesses; ++i){
							if (this->_procId == i)
							{
								tab[i] = true;
								continue;
							}
							pcr_receive_float(i,MCTRL_TAG_ID,l,tab);
							lbbox += BBox3D(fvmath::CVec3f(tab),fvmath::CVec3f(&tab[3]));

							mfp_debug("receiv bbox: min {%f, %f %f} max {%f, %f, %f}\n",tab[0],tab[1],tab[2],tab[3],tab[4],tab[5]);

						}
					}
					else {
						mfp_debug("Sending bbox min = {%f, %f, %f\n",lbbox.Xmin(),lbbox.Ymin(),lbbox.Zmin());
						pcr_send_float(pcr_print_master(),MCTRL_TAG_ID,l,lbbox.data());
					}

					pcr_barrier();
				#endif
				#ifdef _USE_FV_LIB
					//return true;
					return this->Do(INITFIELD);
				#else
					return true;
				#endif
				}
			}
			break;
		case INITMESH:
			{
				return this->InitMesh(str);
			}
			break;
		case INITFIELD:
			{
				if (this->InitField(&_mesh)) {
					this->_pRC = new RContext(ViewManagerInst(),&_mesh,&_field);
					mfvBaseObject::parentMeshPtr = &_mesh;
					mfvBaseObject::parentFieldPtr = &_field;
					mfp_debug("Before building accelstruct\n");
					fv_timer t;
					t.start();
					this->_pRC->accelStruct = new Grid(_pRC);
					//this->_pRC->accelStruct = new BVH(_pRC);
					mfp_debug("Building time: %lf\n",t.get_time_ms());
					//this->Do(UPDATE);
					//this->_field.Draw();
					FV_CHECK_ERROR_GL();
				}
				return true;
			}
		break;
		case UPDATE:
			{
				//Mesh * pm = GetCurrentMesh();
				//if(! pm->IsNotEmpty())
				//{
				//	// Do copy vertex data to GPU
				//	fvmath::Vec3f vertex;
				//	for(int i=1; i <= pm->GetNumNodes(); ++i) 
				//	{
				//		if (pm->GetNodeCoor<float>(i,vertex.v)>0)
				//			pm->GetGLHandle()->AddMeshNode(vertex);
				//	}
				//	// Get the number of vertices
				//	Object*& pobj = pm->GetGLHandle();
				//	const float * pv  = pobj->GetCurrentVtxAccumulator().getVertices().data()->coord();
				//	int numNodes = pobj->GetCurrentVtxAccumulator().getVertices().size();
				//	glGenBuffersARB(2, ViewManagerInst().vbo_id);
				//	glBufferDataARB(GL_ARRAY_BUFFER_ARB, sizeof(float) * 3 * numNodes, pv, GL_STATIC_DRAW_ARB);

				//}
				//if(GetCurrentField()->IsSolutionChanged());
				//if(ViewManagerInst().GetLegend().IsLegendChanged());
				// Set settings
				_mesh.GetGLHandle()->GetCurrentVtxAccumulator().aRenderEdges = ViewManagerInst().GetSettings()->bEdgeOn;
				if (_field.GetApproximationType() == FieldDG) {
					_field.GetGLHandle()->GetCurrentVtxAccumulator().aRenderTraingleStrips = ViewManagerInst().GetSettings()->bShadingOn;
				} else {
					_field.GetGLHandle()->GetCurrentVtxAccumulator().aRenderTraingles = ViewManagerInst().GetSettings()->bShadingOn;
				}
				_pRC->accelStruct->drawOn(ViewManagerInst().GetSettings()->bIsGridDraw);

			}
		break;
		case REFRESH: {
			// Reset field data except hadle to GL conatiners
			_field.Reset(false);
			_field.Clear();
			this->InitField(GetCurrentMesh());	
			//std::cout<< "Current solution formula is: " << this->CurrentSolution.sFormula << std::endl;
			//_field.GetSolution() = this->CurrentSolution;
			//std::cout<< "Current solution formula in field is: " << _field.GetSolution().sFormula << std::endl;
			//this->_field.Draw();
			return true;
		}
		break;
		case RELOAD:
		{
			this->Do(CLEAR);
			return this->Do(INIT);
		}
		break;
		case LEGCHANGE:
		{
			/*VtxAccumulator::NodeColors& colors = _field.GetGLHandle()->GetCurrentVtxAccumulator().getNodeColors();
			VtxAccumulator::NodeColors::size_type size = colors.size();
			Legend& legend = ViewManagerInst().GetLegend();
			//legend.Dump();
			//std::cout<<"Po dump legend\n";
			for (unsigned i = 0; i < size; i++)
			{

				fvmath::Vec3f& col = colors[i];
				double value = legend.GetValue ( ColorRGB( col.x, col.y, col.z));

				if (value < legend.GetStart() || value > legend.GetEnd()) {
					std::cout << "<<<<<<<<<<<<<< = " << value << std::endl;
				}
				std::cout << "i = " << i << std::endl;
				_currLegend.GetColor(value);
				col.x = _currLegend.color.rgb.r;
				col.y = _currLegend.color.rgb.g;
				col.z = _currLegend.color.rgb.b;
				if (col.x ==0 && col.y ==0 && col.z==0) std::cout << "kolor czarny dla wart: " << value << std::endl;
			}
			for(size_t i(0); i < _currLegend.GetColors().size(); ++i)
				std::cout << i <<" = " << _currLegend.GetColors()[i].value << std::endl;
			ViewManagerInst().GetLegend() = _currLegend;
			ViewManagerInst().GetLegend().Update();
			*/
		}
		break;
		case CUTPLANECHANGE:
		{
			if (_field.GetType() == FieldSTD) {

			} else {
				IO::log("For DG cut plane dosen't implemented yet!\n");
			}
		} break;
		case GENERATE_NEW_MESH:
		{

		}
		break;
		case GENERATE_NEW_FIELD:
		{
		}
		break;
		case CLEAR:
		{
			Reset();
		}
		break;
		case NOTHING:
		{
		}
		break;
//#endif
		}// end switch

		return true;
	}

	void ModelCtrl::Reset(bool all_)
	{
		Clear();
		if(all_) this->Destroy();
	}

	void ModelCtrl::Destroy()
	{
		FV_ASSERT(!"Not implemented");
	}

	void ModelCtrl::BeforeRender()
	{
		//_vEGL.clear();
		//Mesh* currMesh = GetTPtr<ModelCtrlMap,Mesh>(_meshes, _mId);

	}


	int  ModelCtrl::TestConnection() const
	{
#ifdef PARALLEL
		const int MCTRL_TAG_ID = 1342;
		const int l = 1;
		mfp_debug("W tescie\n");
		if(this->_procId == pcr_print_master()) {
			assert(_nrProcesses >= 2);
			mfp_debug("waiting\n")
			int *tab = new int[_nrProcesses + 1];
			tab[this->_procId] = this->_procId;
			for(int i = 1; i <= this->_nrProcesses; ++i){
				if (this->_procId == i)
				{
					tab[i] = true;
					continue;
				}
				pcr_receive_int(i,MCTRL_TAG_ID,l,&tab[i]);
			}
			mfp_debug("Checinkg\n")
			// Ceck table if everything is OK
			for(int i = 1; i <= this->_nrProcesses; ++i) {
				bool test = (i == tab[i]);
				mf_test(test, "Master: report incorrect number %d from process %d!\n",tab[i],i);
				if (!test) {
					FV_DELETE_ARRAY(tab);
					return(0);
				}
			}
			FV_DELETE_ARRAY(tab);
			mfp_debug("After checking\n");
		}
		else {
			mfp_debug("Sending\n");
			pcr_send_int(pcr_print_master(),MCTRL_TAG_ID,l,const_cast<int *>(&(this->_procId)));
		}
		mfp_debug("Waiting on barrier\n");
		pcr_barrier();
		mfp_debug("Test connection passed\n");
#endif
		return(1);
	}

	int ModelCtrl::InitHosts(int numHosts)
	{
		FV_ASSERT(numHosts > 0);
		FV_ASSERT(_hosts == 0);

		_hosts = new host_info[numHosts];

		for (int i = 0; i < numHosts; ++i) {
			int size = initOpenCL(&_hosts[i]);
		}

	}


	
} // end namespaces
